#include "client.h"
#include "client_util.h"

//这个函数暂时还没用上****************************
void recvFile(int clientfd){
//先接收文件名
    char filename[100] = {0};
    int len = 0;
    int cmdType = 0;
    int ret = recvn(clientfd, (char*)&len, sizeof(len));//先接长度
    printf("ret: %d, filename's len:%d\n", ret, len);
    ret = recvn(clientfd, (char*)&cmdType, sizeof(cmdType));//再接类型
    ret = recvn(clientfd, filename, len);//再接内容
    printf("ret: %d, recv msg:%s\n", ret, filename);
    
    int wfd = open(filename, O_CREAT | O_RDWR, 0644);

    //再获取的是文件长度
    off_t length = 0;
    recvn(clientfd, (char*)&length, sizeof(length));
    printf("file length: %ld\n", length);
    //最后接收文件内容
    char buff[1000] = {0};
    off_t currSize = 0;
    off_t left = length;
    while(left > 0) {
        left = length - currSize;
        if(left >= 1000) {
            ret = recvn(clientfd, buff, sizeof(buff));//再接文件内容
        } else if(left < 1000 && left > 0){
            ret = recvn(clientfd, buff, left);
        } else {
            break;
        }
        //最后再写入本地
        ret = write(wfd, buff, ret);
        currSize += ret;
    }
    close(wfd);
}

void recvData(int clientfd) 
{
    char data[4096] = {0};
    int len = 0;
    recvn(clientfd, (char*)&len, sizeof(len));//先接长度
    recvn(clientfd, data, len);//再接内容
    printf("%s\n", data);
}

void recvCurr(int clientfd)
{
    curr_info_t curr;
    recvn(clientfd,&curr,sizeof(curr));
    currinfo_printf(curr);
}

int getFile(int clientfd){
    //先接收文件名
    char filename[100] = {0};
    int len = 0;
    recv(clientfd, &len, sizeof(len), 0);
    if(len == -1){
        printf("the file does not exist.\n");
        return 0;
    }

    recvn(clientfd, filename, len);//再接内容
    printf("filename:%s\n", filename);

    //获取当前文件大小
    off_t currLength = 0;
    int fd = open(filename, O_WRONLY);
    if(fd != -1){
        struct stat fileInfo;
        memset(&fileInfo, 0, sizeof(fileInfo));
        fstat(fd, &fileInfo);
        currLength = fileInfo.st_size;
    }
    send(clientfd, &currLength, sizeof(currLength), 0);
    close(fd);

    int wfd = open(filename, O_CREAT | O_RDWR, 0644);
    ERROR_CHECK(wfd, -1, "open");

    //再获取的是文件长度
    off_t length = 0;
    recvn(clientfd, (char*)&length, sizeof(length));
    printf("file length: %ld\n", length);

    //对文件进行偏移
    int ret = lseek(wfd, currLength, SEEK_SET);
    if(ret != 0){
        perror("lseek");
    }
    length -= currLength;

    //最后接收文件内容
    //接收小文件
    if(length < 104857600){
        off_t splice = length / 100;
        off_t currSize = 0;
        off_t lastSize = 0;
        char buff[1000] = {0};
        off_t left = length;
        while(left > 0) {
            left = length - currSize;
            if(left >= 1000) {
                ret = recvn(clientfd, buff, sizeof(buff));//再接文件内容
            } else if(left < 1000 && left > 0){
                ret = recvn(clientfd, buff, left);
            } else {
                break;
            }
            //最后再写入本地
            ret = write(wfd, buff, ret);
            currSize += ret;
            if(currSize - lastSize > splice) {
                printf("has complete %5.2lf%%\r", (double)currSize / length * 100);
                fflush(stdout);
                lastSize = currSize;
            }
        }
        printf("has complete 100.00%%\n");
        close(wfd);
        return 0;
    }
    //接收大文件
    off_t offset = 0;
    off_t splice = 4096 * 128;
    off_t Splice = length / 100;
    off_t lastSize = 0;
    ftruncate(wfd, length);
    while(offset < length){
        if(offset + splice > length){
            splice = length - offset;
        }
        char * pMap = (char*)mmap(NULL, splice, PROT_READ|PROT_WRITE, MAP_SHARED, wfd, offset);
        if(pMap) {//映射成功
            //clientfd代表的是内核态套接字的缓冲区
            //pMap代表的是内核文件缓冲区
            int ret = recv(clientfd, pMap, splice, MSG_WAITALL);
            offset += ret;
            if(offset - lastSize > Splice){
                printf("has complete %5.2lf%%\r", (double)offset / length * 100);
                fflush(stdout);
                lastSize = offset;
            }
        }
    }
    printf("has complete 100.00%%\n");
    printf("recv file over.\n");
    close(wfd);

    return 0;
}

int putFile(int sockfd, char* filename){
    //进行文件的发送
    //先发送文件名
    int fd = open(filename, O_RDWR);
    int len = strlen(filename);
    if(fd == -1){
        len =-1;
        perror("open");
        send(sockfd, &len, 4, 0);
        return 0;
    }
    send(sockfd, &len, sizeof(len), 0);
    sendn(sockfd, filename, len);
    printf("filename: %s\n", filename);

    //获取文件的长度
    struct stat fileInfo;
    memset(&fileInfo, 0, sizeof(fileInfo));
    fstat(fd, &fileInfo);
    off_t length = fileInfo.st_size;
    printf("file length: %ld\n", length);
    //发送文件的长度
    sendn(sockfd, &length, sizeof(length));

    //获取文件MD5码
    int md5_fd = open(filename, O_RDONLY);
    if(md5_fd == -1){
        perror("md5_open");
        return 0;
    }
    //获取文件MD5码
    char md5[33] = {0};
    //get_file_md5(filename, (unsigned char*)md5);

    char buff[1000] = {0};

    MD5_CTX ctx;
    MD5_Init(&ctx);//初始化MD5结构体
    while(1){
        memset(buff, 0, sizeof(buff));
        //读取文件中一段内容
        int ret = read(md5_fd, buff, sizeof(buff));
        if(ret == 0){
            break;
        }
        //对每一段内容调用更新函数
        MD5_Update(&ctx, buff, ret);
    }
    unsigned char md[16] = {0};
    MD5_Final(md, &ctx);//结束
    close(md5_fd);
    for(int i = 0; i < 16; i++){
        char frag[3] = {0};
        sprintf(frag, "%02x", md[i]);
        strcat(md5, frag);
    }

    printf("md5: %s\n", md5);
    //发送MD5码
    int rett = sendn(sockfd, md5, strlen(md5));
    if(rett == -1){
        printf("failed to send md5.\n");
    }

    //接收是否需要发送文件内容的信号
    int sig = 0;
    recv(sockfd, &sig, sizeof(sig), 0);
    printf("sig: %d\n", sig);
    if(sig == 0){//秒传
        printf("second transmission.\n");
        return 0;
    }

    //发送文件内容
    printf("start sending file.\n");
    //发送小文件
    if(length < 104857600){
        //char buff[1000] = {0};
        off_t currSize = 0;
        off_t lastSize = 0;
        off_t splice = length / 100;
        while(1){
            memset(&buff, 0, sizeof(buff));
            int ret = read(fd, buff, sizeof(buff));
            if(ret <= 0){
                break;
            }
            ret = sendn(sockfd, buff, strlen(buff));
            if(ret == -1){
                break;
            }
            currSize += ret;
            if(currSize - lastSize > splice){
                printf("has complete %5.2lf%%\r", (double)currSize / length * 100);
                fflush(stdout);
                lastSize = currSize;
            }
        }
        printf("has complete 100.00%%\n");
        printf("send file over.\n");
        close(fd);
        return 0;
    }

    //发送大文件
    off_t offset = 0;
    off_t splice = 4096 * 128;
    off_t Splice = length / 100;
    off_t lastSize = 0;
    while(offset < length){
        char * pMap = (char*)mmap(NULL, splice, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
        if(pMap) {
             //pMap代表的是内核中的文件缓冲区
             //sockfd代表的是套接字缓冲区
             int ret = send(sockfd, pMap, splice, 0);
             offset += ret;
            if(offset - lastSize > Splice){
                printf("has complete %5.2lf%%\r", (double)offset / length * 100);
                fflush(stdout);
                lastSize = offset;
            }
        }
    }
    printf("has complete 100.00%%\n");
    printf("send file over.\n");
    close(fd);//关闭文件
    return 0;
}
